extends CharacterBody3D

# общий скрипт для всех чужих кораблей


const QueryObject = preload("queries.gd")


var uuid = ""
var objekto
var distance = 0 # дистанция до корабля игрока
var integreco = 100 # целостность объекта
var potenco

var acceleration = 1 # для расчета движения корабля
var target_rot #положение корабля, которое надо принять, чтобы нацелиться на точку.
var target_dir: Vector3 = Vector3.ZERO #направление на эту точку от текущей позиции корабля
var speed_rotation =1

# максимальная скорость корабля
var max_speed =50.0
# текущая скорость
var current_speed =0


# uuid проекта маршрута
var projekto_itineroj_uuid
#маршрут движения корабля
var itineroj = [] 
# uuid - uuid задачи
# front - transform координат цели движения (лучше высчитать один раз при добавлении)
# координаты цели полёта
#			'koordinatoX':
#			'koordinatoY':
#			'koordinatoZ':
# pozicio - в какой позиции должна находится задача
var pause_itinero = true # корабль стоит на паузе

# взятые в прицел, массив объектов, взятых в прицел
var celilo = [] # список целей структурой:
# objekto - указатель на объект-цель
# tasko_uuid - uuid задачи прицеливания
var pafado_uuid # uuid проекта ведения огня
var armiloj = [] # ссылка на оружие корабля
var distanco_celilo = 1000 # максимальное расстояние до цели (distanco - расстояние)


func _ready():
	$CollisionShape3D.queue_free()


func _physics_process(delta):
	if !(itineroj.is_empty() or pause_itinero): #Если цель существует, двигаемся
#		print('летит объект = ',uuid)
		var front = Transform3D(Basis.IDENTITY,Vector3(itineroj.front()['koordinatoX'],
			itineroj.front()['koordinatoY'], itineroj.front()['koordinatoZ']))
		target_dir = (front.origin - position).normalized()
		var distance_target = position.distance_to(front.origin)
		if (len (itineroj)>1) and \
				(itineroj[1]['kategorio']==Net.kategorio_movado):
			if distance_target <1: #Если это не последняя точка, то мы не тормозим, так что завышаем расстояние, чтобы не проскочить эту точку
				dec_route()
				return
			current_speed = lerp(current_speed,max_speed,delta*acceleration)#ускоряемся
			transform.basis = transform.basis.slerp(target_rot,speed_rotation*delta) #потихоньку поворачиваем корабль на цель. Взято у Сканера из урока про зомбей. Quaternion(transform.basis) - текущий поворот корабля
		else:
			#проверяем поворот в сторону цели
			pass
			
			
#			if distance_target > max_speed*delta/acceleration:#Тут сомнительная формула от фонаря, вычисляющая примерно откуда надо начинать тормозить корабль.
			if distance_target > max_speed*acceleration:#Тут сомнительная формула от фонаря, вычисляющая примерно откуда надо начинать тормозить корабль.
				current_speed = lerp(current_speed,max_speed,delta*acceleration)#ускоряемся
				var a = Quaternion(transform.basis)
				var c = a.slerp(target_rot,speed_rotation*delta) #потихоньку поворачиваем корабль на цель. Взято у Сканера из урока про зомбей. Quaternion(transform.basis) - текущий поворот корабля
				transform.basis = Basis(c)
#				transform.basis = transform.basis.slerp(target_rot,speed_rotation*delta) #потихоньку поворачиваем корабль на цель. Взято у Сканера из урока про зомбей. Quat(transform.basis) - текущий поворот корабля
			else: #Если это  последняя точка, то мы тормозим, и задаём минимальное расстояние, чтобы точнее выставить корабль
#				current_speed = lerp(current_speed,50,delta*acceleration)#замедляемся
				current_speed = lerp(current_speed,1,delta*acceleration)#замедляемся
				if front.basis !=Basis.IDENTITY:
					transform.basis = transform.basis.slerp(Quaternion(front.basis),speed_rotation*delta*1.5) #поворачиваем в дефолтное состояние, чтобы сесть
				if distance_target <0.01:
					if front.basis !=Basis.IDENTITY:
						transform.basis = front.basis
					position = front.origin
					route_gone()
					return
		set_velocity(target_dir*current_speed)
		move_and_slide()# по инструкции delta не должно быть
	# если сервер и есть цели, то проверяем дистанцию до цели и если привышает допустимую - закрываем заадчу цели
	if Global.server and len(celilo)>0:
		for celo in celilo:
			var distance_celo = position.distance_to(celo['objekto'].transform.origin)
			if distance_celo>distanco_celilo:
				# закрываем задачу прицеливания
				forigo_celo(celo['objekto'])


func rotate_start():# поворачиваем корабль носом по ходу движения
	var front = Transform3D(Basis.IDENTITY, Vector3(itineroj.front()['koordinatoX'],
		itineroj.front()['koordinatoY'], itineroj.front()['koordinatoZ']))
	target_rot = Quaternion(transform.looking_at(front.origin,Vector3.UP).basis) #запоминаем в какое положение надо установить корабль, чтобы нос был к цели. Это в кватернионах. ХЗ что это, но именно так вращать правильнее всего.


#func set_route(route_array):
#	if !route_array.empty():
#		itineroj.clear() #Просто приравнивать нельзя, так как изменяется адрес и если где-то на него кто-то ссылался в других сценах, то он теряет этот адрес Так что очищаем и копируем.
#		for i in route_array:
#			itineroj.push_back(i)
#		rotate_start()


func dec_route():
	if Global.server:
		# отправляем на сервер закрытие задачи
		if len(itineroj)>0 and itineroj.front()['uuid_tasko']:
			Net.send_json(Queries.finado_tasko(itineroj.front()['uuid_tasko']))
	itineroj.pop_front()
	rotate_start()
#	set_route(itineroj.duplicate())


func fermi_projekto_tasko():
	projekto_itineroj_uuid = ''
	sxanghi_pause(true)
	itineroj.clear()
	current_speed = 0


func route_gone():
	if Global.server:
		# отправляем на сервер закрытие задачи и проекты
		if len(itineroj)>0 and itineroj.front()['uuid_tasko']:
			Net.send_json(Queries.finado_projekto_tasko(projekto_itineroj_uuid,itineroj.front()['uuid_tasko']))
		elif projekto_itineroj_uuid:
			Net.send_json(Queries.finado_projekto(projekto_itineroj_uuid))
	fermi_projekto_tasko()

#func add_route(route_array):
#	for i in route_array:
#		itineroj.push_back(i)
#	set_route(itineroj.duplicate())


# перевод параметров в одинаковый массив
func array_itinero(uuid_tasko, uuid_celo, nomo, priskribo, koordinatoX, 
	koordinatoY, koordinatoZ, transform, distance_target, kategorio, 
	pozicio):
	return {
		'uuid_tasko':uuid_tasko,
		'uuid_celo':uuid_celo,
		'nomo':nomo,
		'priskribo':priskribo,
		'koordinatoX':koordinatoX,
		'koordinatoY':koordinatoY,
		'koordinatoZ':koordinatoZ,
		'transform': transform,
		'distance':distance_target,
		'kategorio':kategorio,
		'pozicio':pozicio
	}

# добавить точку маршрута 
func add_itinero(uuid_tasko, uuid_celo, nomo, priskribo, koordinatoX, 
	koordinatoY, koordinatoZ, transform, distance_target, kategorio, 
	pozicio):
	itineroj.append(array_itinero(uuid_tasko, uuid_celo, nomo, priskribo, koordinatoX, 
		koordinatoY, koordinatoZ, transform, distance_target, kategorio, 
		pozicio))


# очищаем маршрут
func malplenigi_itinero():
	route_gone()

# редактировать маршрут
func sxangxi_itinero(projekto, tasko):
#	if (tasko['objekto']) and (uuid != tasko['objekto']['uuid']):

	if (Net.kategorio_eniri_kosmostacio==tasko['kategorio']['edges'].front()['node']['objId']): # задача входа в станцию, пропускаем обработку
		return

	var nomo = tasko['nomo']['enhavo']
	var priskribo = tasko['priskribo']['enhavo']
	var vector = 0
	if tasko['finKoordinatoX']:
		vector = Vector3(tasko['finKoordinatoX'],
			tasko['finKoordinatoY'], 
			tasko['finKoordinatoZ'])
	if projekto['uuid']==projekto_itineroj_uuid: # изменение по задаче в текущем проекте
		if projekto['statuso']['objId'] == Net.statuso_fermo:
			# закрытие проекта
			fermi_projekto_tasko()
		var new_tasko = true # признак необходимости новой задачи
		var pos = 0 # номер позиции в списке задач
		for it in itineroj:# проходим по всем задачам
			if tasko['uuid'] == it['uuid_tasko']: # нашли соответствующую задачу
				new_tasko = false
				if tasko['statuso']['objId']==Net.statuso_nova: # новая -  в очередь выполнения 
					it['koordinatoX'] = tasko['finKoordinatoX']
					it['koordinatoY'] = tasko['finKoordinatoY']
					it['koordinatoZ'] = tasko['finKoordinatoZ']
				elif tasko['statuso']['objId']==Net.statuso_laboranta: # в работе
					# задача должна быть первой в списке
					if pos: # если не первая
						itineroj.remove_at(pos)
						itineroj.push_front(array_itinero(
							tasko['uuid'],
							tasko['objekto']['uuid'] if tasko['objekto'] else '',
							nomo, 
							priskribo,
							tasko['finKoordinatoX'],
							tasko['finKoordinatoY'],
							tasko['finKoordinatoZ'],
							Transform3D(Basis.IDENTITY, vector),
							position.distance_to(vector),
							tasko['kategorio']['edges'].front()['node']['objId'],
							tasko['pozicio']
						))
					else:
						it['koordinatoX'] = tasko['finKoordinatoX']
						it['koordinatoY'] = tasko['finKoordinatoY']
						it['koordinatoZ'] = tasko['finKoordinatoZ']
					# отправляем корабль на уточнённые координаты, а точнее поворачиваем
					rotate_start()
					sxanghi_pause(false)
				elif tasko['statuso']['objId']==Net.statuso_fermo: # закрыта
					#удаляем из списка
					itineroj.remove_at(pos)
					if len(itineroj)>0:
						rotate_start()
				elif tasko['statuso']['objId']==Net.status_pauzo: # приостановлена
					sxanghi_pause(true)
				break
			pos += 1
		if new_tasko: # добавляем новую задачу в проект
			if tasko['statuso']['objId']==Net.statuso_nova: # новая -  в очередь выполнения 
				# нужно выстроить по очерёдности
				pos = 0
				var pozicio = false
				for it in itineroj:# проходим по всем задачам и находим нужную позицию
					if it['pozicio']>tasko['pozicio']: # вставляем перед данной записью
						itineroj.insert(pos, array_itinero(
							tasko['uuid'],
							tasko['objekto']['uuid'] if tasko['objekto'] else '',
							nomo, 
							priskribo,
							tasko['finKoordinatoX'],
							tasko['finKoordinatoY'],
							tasko['finKoordinatoZ'],
							Transform3D(Basis.IDENTITY, vector),
							position.distance_to(vector),
							tasko['kategorio']['edges'].front()['node']['objId'],
							tasko['pozicio']
						))
						pozicio = true
						break
					pos += 1
				if !pozicio: # позиция не найдены, добавляем в конце
					itineroj.push_back(array_itinero(
							tasko['uuid'],
							tasko['objekto']['uuid'] if tasko['objekto'] else '',
							nomo, 
							priskribo,
							tasko['finKoordinatoX'],
							tasko['finKoordinatoY'],
							tasko['finKoordinatoZ'],
							Transform3D(Basis.IDENTITY, vector),
							position.distance_to(vector),
							tasko['kategorio']['edges'].front()['node']['objId'],
							tasko['pozicio']
						))
			elif tasko['statuso']['objId']==Net.statuso_laboranta: # в работе
					# задача должна быть первой в списке
				itineroj.push_front(array_itinero(
							tasko['uuid'],
							tasko['objekto']['uuid'] if tasko['objekto'] else '',
							nomo, 
							priskribo,
							tasko['finKoordinatoX'],
							tasko['finKoordinatoY'],
							tasko['finKoordinatoZ'],
							Transform3D(Basis.IDENTITY, vector),
							position.distance_to(vector),
							tasko['kategorio']['edges'].front()['node']['objId'],
							tasko['pozicio']
						))
					# отправляем корабль на уточнённые координаты, а точнее поворачиваем
				rotate_start()
				sxanghi_pause(false)
			elif tasko['statuso']['objId']==Net.status_pauzo: # приостановлена
				itineroj.push_front(array_itinero(
							tasko['uuid'],
							tasko['objekto']['uuid'] if tasko['objekto'] else '',
							nomo, 
							priskribo,
							tasko['finKoordinatoX'],
							tasko['finKoordinatoY'],
							tasko['finKoordinatoZ'],
							Transform3D(Basis.IDENTITY, vector),
							position.distance_to(vector),
							tasko['kategorio']['edges'].front()['node']['objId'],
							tasko['pozicio']
						))
				sxanghi_pause(true)
	else: # первая запись нового проекта
		if tasko['statuso']['objId']!=Net.statuso_fermo:
			route_gone()
			projekto_itineroj_uuid = projekto['uuid']
			itineroj.push_back(array_itinero(
							tasko['uuid'],
							tasko['objekto']['uuid'] if tasko['objekto'] else '',
							nomo, 
							priskribo,
							tasko['finKoordinatoX'],
							tasko['finKoordinatoY'],
							tasko['finKoordinatoZ'],
							Transform3D(Basis.IDENTITY, vector),
							position.distance_to(vector),
							tasko['kategorio']['edges'].front()['node']['objId'],
							tasko['pozicio']
						))
			if tasko['statuso']['objId']==Net.statuso_laboranta: # в работе
				rotate_start()
				sxanghi_pause(false)
			else:
				sxanghi_pause(true)


# пришла с сервера задача выстрела
func pafo(projekto, tasko):
#	=== в стрельбу попали 
	# если стрельба
	if tasko['kategorio']['edges'].front()['node']['objId']==Net.tasko_kategorio_pafo:
		# циклом пробегаемся по armiloj
		# находим оружие и передаём ему цель стрельбы
		for armilo in armiloj:
			# ищем, какое именно оружие стреляло
			if armilo.uuid == tasko['posedanto']['edges'].front()['node']['posedantoObjekto']['uuid']:
				# что делать - стрелять в цель, или прекращать стрелять
				if tasko['statuso']['objId']==Net.statuso_laboranta:
					# находим по uuid цель в списке объектов космоса
					if tasko['objekto']:
						armilo.set_target(Global.fenestro_kosmo.sercxo_objekto_uuid(tasko['objekto']['uuid']))
				else:
					# сбрасываем цель стрельбы в null
					armilo.set_target(null)
	elif tasko['kategorio']['edges'].front()['node']['objId']==Net.tasko_kategorio_celilo:
		print('=== попали в задачу прицеливания projekto=',projekto)
		# если открытие задачи
		if tasko['statuso']['objId']==Net.statuso_laboranta:
			add_celo(
				Global.fenestro_kosmo.sercxo_objekto_uuid(tasko['objekto']['uuid']),
				tasko['uuid']
			)
		else: # снимаем задачу прицеливания
			forigo_celo(
				Global.fenestro_kosmo.sercxo_objekto_uuid(tasko['objekto']['uuid'])
			)
	else:
		print('=== попали не в стрельбу projekto=',projekto)
		print(' ===tasko=',tasko)


# warning-ignore:unused_argument
# warning-ignore:unused_argument
# warning-ignore:unused_argument
# warning-ignore:unused_argument
func _on_sxipo_input_event(camera, event, click_position, click_normal, shape_idx):
	if Global.fenestro_kosmo:
		# нажатие левой кнопки мыши
		if event.is_action_pressed("left_click"):
			Global.fenestro_kosmo.node_objekto.set_objekto(self)


#изменить паузу
func sxanghi_pause(paus):
	if paus:
		pause_itinero = true
		$timer.stop()
	else:
		pause_itinero = false
		$timer.start()


func add_celo(celo, tasko_uuid):
	celilo.append({
		'objekto': celo,
		'tasko_uuid': tasko_uuid
	})


# снять с прицеливания
# celo - указатель на объект прицеливания
func forigo_celo(objekto_celo):
	if !Global.server:
		return false
	if !objekto_celo.get('uuid'):
		return false
	if celilo.size()<1:
		return false
	for celo in celilo:
		if celo['objekto'] == objekto_celo:
			# если снимаем данную цель
			# закрываем задачу
			Net.send_json(Queries.finado_tasko(celo['tasko_uuid'], Net.statuso_fermo))
			print('=== закрываем задачу')
	# если идёт стрельба по данному объекту, то прекращаем
	for armilo in armiloj:
		armilo.set_target(null)
	celilo.clear()
	# закрываем проект
	forigo_projekto_pafado()


# закрытие проекта стрельбы
func forigo_projekto_pafado():
	if !Global.server:
		return false
	if !pafado_uuid: # если нет проекта стрельбы
		print('=== нет проекта стрельбы')
		return false
	if len(celilo)>0:
		return false
	print('=== закрываем проект')
	Net.send_json(Queries.finado_projekto(pafado_uuid))


func _on_timer_timeout():
	if !Global.server:
		return
	if pause_itinero: # на паузе передавать данные не надо
		return
	var q = QueryObject.new()
	# проверить на изменение координат и если было изменение - отправляем
	# бывает изменений нет - упёрлись в препятствие
	if !((rotation == Vector3(objekto['rotaciaX'],
			objekto['rotaciaY'], objekto['rotaciaZ'])) and\
			(position == Vector3(objekto['koordinatoX'],
			objekto['koordinatoY'], objekto['koordinatoZ']))):
#		if Global.logs:
#			print('===_on_timer_timeout =',pause_itinero,', uuid=',uuid)
#			print('===rotation=',rotation)
#			print('===rotation 2 =',objekto['rotaciaX'],',',objekto['rotaciaY'],',',objekto['rotaciaZ'],',')
#			print('===translation=',translation)
#			print('===translation 2 =',objekto['koordinatoX'],',',objekto['koordinatoY'],',',objekto['koordinatoZ'],',')
		Net.send_json(q.objecto_mutation(uuid, position.x, 
				position.y, position.z,
				rotation.x, rotation.y, rotation.z
		))



